﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Linq;
using System.Text;
using ActivateYourGlutes.Infrastructure;
using ActivateYourGlutes.Model.Contracts;
using AutoMapper;

namespace ActivateYourGlutes.Model.Providers
{
    public class MeasurementTypeProvider : ProviderBase, IMeasurementTypeProvider
    {
        public MeasurementTypeProvider(IEntityModelFactory factory) : base(factory)
        {
        }

        /// <summary>
        /// Returns a list of the measurement types that can be logged against
        /// </summary>
        public List<MeasurementType> MeasurementTypes(Contracts.Account forAccount)
        {
            var measurementTypes = Db.MeasurementTypeSet
                .Include("UserDefinedTypeOwner")
                .Where(mt => mt.UserDefinedTypeOwner == null || mt.UserDefinedTypeOwner.AccountId == forAccount.AccountId)
                .ToList();
            return Mapper.Map<List<Internal.MeasurementType>, List<MeasurementType>>(measurementTypes);
        }

        /// <summary>
        /// Returns the value types that a meaurement can be taken with
        /// </summary>
        public List<MeasurementTypeValueType> ValueTypes()
        {
            return MeasurementTypeValueType.All();
        }

        /// <summary>
        /// Adds the given measurement type to database and assigns it to the account
        /// </summary>
        public void AddToAccount(Account account, MeasurementType measurementType)
        {
            var internalMeasurementType = Mapper.Map<MeasurementType, Internal.MeasurementType>(measurementType);
            NameValueCollection errors = internalMeasurementType.Validate();
            if (errors.Count > 0)
                throw new ValidationException(errors);
            // check for duplicates
            var duplicate = Db.MeasurementTypeSet
                .Where(
                mt => (mt.UserDefinedTypeOwner == null || mt.UserDefinedTypeOwner.AccountId == account.AccountId) &&
                      mt.Description.ToLower() == internalMeasurementType.Description.ToLower()).Any();
            if (duplicate)
                throw new ValidationException("_FORM", "You already have that measurement type.");
            internalMeasurementType.DisplayOrder = Db.MeasurementTypeSet
                                                       .Where(
                                                       mt =>
                                                       mt.UserDefinedTypeOwner == null ||
                                                       mt.UserDefinedTypeOwner.AccountId == account.AccountId)
                                                       .Max(mt => mt.DisplayOrder) + 1;
            internalMeasurementType.UserDefinedTypeOwnerReference.EntityKey = new EntityKey("ActivateYourGlutesEntities.AccountSet", "AccountId", account.AccountId);
            Db.AddToMeasurementTypeSet(internalMeasurementType);
            Db.SaveChanges();
        }

        /// <summary>
        /// Gets an account by ID
        /// </summary>
        public MeasurementType GetById(Account accessingAccount, int id)
        {
            var measurementType = Db.MeasurementTypeSet.Include("UserDefinedTypeOwner").Where(mt => mt.MeasurementTypeId == id).First();
            if (measurementType.UserDefinedTypeOwner != null && measurementType.UserDefinedTypeOwner.AccountId != accessingAccount.AccountId)
                throw new AuthorizationException("You do not have rights to view this measurement type");
            return Mapper.Map<Internal.MeasurementType, MeasurementType>(measurementType);
        }

        /// <summary>
        /// Deletes a measurement type identified by id
        /// </summary>
        public void Delete(Account accessingAccount, int id)
        {
            var measurementType = Db.MeasurementTypeSet
                .Include("UserDefinedTypeOwner")
                .Include("UserDefinedTypeOwner.PrimaryMeasurementType")
                .Include("UserDefinedTypeOwner.SecondaryMeasurementType")
                .Include("MeasurementSet")
                .Include("GoalSet")
                .Include("MeasurementSet.Body")
                .Where(mt => mt.MeasurementTypeId == id)
                .First();

            if ((measurementType.UserDefinedTypeOwner != null && measurementType.UserDefinedTypeOwner.AccountId != accessingAccount.AccountId) || measurementType.UserDefinedTypeOwner == null)
                throw new AuthorizationException("You do not have rights to view this measurement type");

            foreach(var measurement in measurementType.MeasurementSet.ToList())
            {
                Db.DeleteObject(measurement);
            }

            foreach(var goal in measurementType.GoalSet.ToList())
            {
                Db.DeleteObject(goal);
            }

            if (measurementType.UserDefinedTypeOwner.PrimaryMeasurementType == measurementType)
                measurementType.UserDefinedTypeOwner.PrimaryMeasurementType = null;
            if (measurementType.UserDefinedTypeOwner.SecondaryMeasurementType == measurementType)
                measurementType.UserDefinedTypeOwner.SecondaryMeasurementType = null;

            Db.DeleteObject(measurementType);

            Db.SaveChanges();
        }
    }
}
